﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Concurrent;
using System.Windows.Data;
using System.ComponentModel;
using System.Collections.Generic;

namespace Algobox.Toolbox.Collections
{
    public class ConcurrentObservableDictionary<TKey, TValue> where TValue : INotifyPropertyChanged
    {
        public ConcurrentObservableDictionary()
        {
            _items = new ConcurrentDictionary<TKey, TValue>();
            _observableItems = new ConcurrentObservableCollection<TValue>();
            View = (ListCollectionView)CollectionViewSource.GetDefaultView(_observableItems);
            
        }

        private ConcurrentObservableCollection<TValue> _observableItems;
        private ConcurrentDictionary<TKey, TValue> _items;

        public ListCollectionView View { get; private set; }

        public ICollection<TKey> Keys { get { return _items.Keys; } }
        public ICollection<TValue> Values { get { return _items.Values; } }

        public TValue AddOrUpdate(TKey key, TValue value)
        {
            return _items.AddOrUpdate(key,
                (mapKey) =>
                {
                    _observableItems.Add(value);
                    return value;
                },
                (mapKey, mapValue) =>
                {
                    _observableItems.Remove(mapValue);
                    _observableItems.Add(value);
                    return value;
                }
            );            
        }

        public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
        {
            return _items.AddOrUpdate(key,
                (mapKey) =>
                {
                    TValue value = addValueFactory(mapKey);
                    _observableItems.Add(value);
                    return value;
                },
                (mapKey, mapValue) =>
                {
                    _observableItems.Remove(mapValue);
                    TValue value = updateValueFactory(mapKey, mapValue);
                    _observableItems.Add(value);
                    return value;
                }
            );
        }

        public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory)
        {
            return _items.AddOrUpdate(key,
                (mapKey) =>
                {
                    TValue value = addValueFactory(mapKey);
                    _observableItems.Add(value);
                    return value;
                },
                (mapKey, mapValue) =>
                {
                    return mapValue;
                }
            );
        }

        public bool TryAdd(TKey key, TValue item)
        {
            if (_items.TryAdd(key, item))
            {
                _observableItems.Add(item);
                return true;
            }
            return false;
        }

        public bool TryGetValue(TKey key, out TValue item)
        {
            return _items.TryGetValue(key, out item);
        }

        public bool TryRemove(TKey key)
        {
            TValue item;
            return this.TryRemove(key, out item);
        }

        public bool TryRemove(TKey key, out TValue item)
        {
            if (_items.TryRemove(key, out item))
            {
                _observableItems.Remove(item);
                return true;
            }
            return false;           
        }

        public void Clear()
        {
            _items.Clear();
            _observableItems.Clear();
        }


        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _items.GetEnumerator();
        }


        public bool ContainsKey(TKey obj)
        {
            return _items.ContainsKey(obj);
        }
    }
}
